#!/usr/bin/env python3
"""
字体解码工具

用于处理网站自定义字体反爬虫机制，通过解析字体文件来解码乱码文字。
符合openUBMC社区的开发规范，避免魔鬼数字，使用常量定义。
"""

import re
import json
import requests
from pathlib import Path
from typing import Dict, Optional, List
from urllib.parse import urljoin, urlparse
import logging

from .font_constants import (
    UnicodeRanges, HttpConstants, FontConstants, TextAnalysisConstants,
    RegexPatterns, BrowserConstants, OCRConstants, ErrorCodes,
    PerformanceConstants, SystemFonts, FileConstants, DebugConstants
)

logger = logging.getLogger(__name__)


class FontDecoder:
    """字体解码器"""

    def __init__(self, cache_dir: str = FontConstants.DEFAULT_CACHE_DIR):
        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(exist_ok=True)
        self.font_mappings = {}

    def extract_font_urls(self, html_content: str, base_url: str) -> List[str]:
        """从HTML中提取字体文件URL"""
        font_urls = []

        # 查找@font-face规则
        matches = re.findall(RegexPatterns.FONT_FACE_PATTERN, html_content, re.IGNORECASE)

        for match in matches:
            font_url = urljoin(base_url, match)
            if font_url not in font_urls:
                font_urls.append(font_url)

        # 查找link标签中的字体文件
        matches = re.findall(RegexPatterns.FONT_LINK_PATTERN, html_content, re.IGNORECASE)

        for match in matches:
            font_url = urljoin(base_url, match)
            if font_url not in font_urls:
                font_urls.append(font_url)

        logger.info(f"找到 {len(font_urls)} 个字体文件")
        return font_urls

    def download_font_file(self, font_url: str) -> Optional[Path]:
        """下载字体文件"""
        try:
            # 生成缓存文件名
            font_name = Path(urlparse(font_url).path).name
            cache_file = self.cache_dir / font_name

            # 如果已缓存，直接返回
            if cache_file.exists():
                logger.info(f"使用缓存的字体文件: {font_name}")
                return cache_file

            # 下载字体文件
            logger.info(f"下载字体文件: {font_url}")
            headers = {
                'User-Agent': BrowserConstants.DEFAULT_USER_AGENT,
                'Referer': 'https://zxts.zjzwfw.gov.cn/'
            }
            response = requests.get(
                font_url,
                timeout=HttpConstants.DEFAULT_TIMEOUT,
                headers=headers
            )
            response.raise_for_status()

            # 检查文件大小
            content_length = len(response.content)
            if content_length > FontConstants.MAX_FONT_FILE_SIZE:
                logger.warning(f"字体文件过大: {content_length} bytes")
                return None

            # 保存到缓存
            with open(cache_file, 'wb') as f:
                f.write(response.content)

            logger.info(f"字体文件已保存: {cache_file}")
            return cache_file

        except Exception as e:
            logger.error(f"下载字体文件失败 {font_url}: {e}")
            return None

    def parse_woff_font(self, font_path: Path) -> Dict[str, str]:
        """解析WOFF字体文件"""
        try:
            from fontTools.ttLib import TTFont

            font = TTFont(font_path)
            cmap = font.getBestCmap()

            # 建立字符映射关系 - 改进版本
            char_mapping = {}

            # 获取字体的实际字符映射
            for unicode_val, glyph_name in cmap.items():
                char = chr(unicode_val)

                # 尝试从glyph_name推断真实字符
                # 通常glyph_name包含Unicode信息
                if 'uni' in glyph_name:
                    # 提取Unicode值
                    uni_match = re.search(RegexPatterns.UNI_PATTERN, glyph_name, re.IGNORECASE)
                    if uni_match:
                        try:
                            real_unicode = int(uni_match.group(1), 16)
                            real_char = chr(real_unicode)
                            char_mapping[char] = real_char
                        except (ValueError, OverflowError):
                            char_mapping[char] = glyph_name
                else:
                    char_mapping[char] = glyph_name

            logger.info(f"解析字体文件完成，找到 {len(char_mapping)} 个字符映射")
            return char_mapping

        except ImportError:
            logger.error("需要安装 fontTools: pip install fonttools")
            return {}
        except Exception as e:
            logger.error(f"解析字体文件失败: {e}")
            return {}

    def create_font_mapping_table(self, font_urls: List[str], base_url: str) -> Dict[str, str]:
        """创建字体映射表"""
        mapping_table = {}

        for font_url in font_urls:
            font_path = self.download_font_file(font_url)
            if font_path:
                char_mapping = self.parse_woff_font(font_path)
                mapping_table.update(char_mapping)

        return mapping_table

    def decode_text(self, garbled_text: str, mapping_table: Dict[str, str]) -> str:
        """解码乱码文字"""
        if not garbled_text or not mapping_table:
            return garbled_text

        decoded_text = ""
        for char in garbled_text:
            if char in mapping_table:
                decoded_text += mapping_table[char]
            else:
                decoded_text += char

        return decoded_text


class OCRDecoder:
    """OCR文字识别解码器"""

    def __init__(self):
        self.ocr_available = False
        try:
            import easyocr
            self.reader = easyocr.Reader(OCRConstants.SUPPORTED_LANGUAGES)
            self.ocr_available = True
            logger.info("OCR解码器初始化成功")
        except ImportError:
            logger.warning("需要安装 easyocr: pip install easyocr")
        except Exception as e:
            logger.error(f"OCR解码器初始化失败: {e}")

    def decode_text_with_ocr(self, driver, element_selector: str) -> str:
        """使用OCR识别文字"""
        if not self.ocr_available:
            return ""

        try:
            from selenium.webdriver.common.by import By
            from PIL import Image
            import io

            # 获取元素
            element = driver.find_element(By.CSS_SELECTOR, element_selector)

            # 截取元素区域
            screenshot = driver.get_screenshot_as_png()
            image = Image.open(io.BytesIO(screenshot))

            # 获取元素位置和大小
            location = element.location
            size = element.size

            # 裁剪图片
            left = location['x']
            top = location['y']
            right = location['x'] + size['width']
            bottom = location['y'] + size['height']

            cropped_image = image.crop((left, top, right, bottom))

            # OCR识别
            results = self.reader.readtext(cropped_image)

            # 提取文字
            text = " ".join([result[1] for result in results])

            return text

        except Exception as e:
            logger.error(f"OCR识别失败: {e}")
            return ""


class FontReplacer:
    """字体替换器"""

    def __init__(self):
        self.system_fonts = SystemFonts.FALLBACK_FONTS

    def inject_font_replacement_script(self, driver) -> None:
        """注入字体替换脚本"""
        # 构建字体列表字符串
        font_list = ", ".join([f'"{font}"' for font in self.system_fonts])

        script = f"""
        // 禁用所有自定义字体
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
            * {{
                font-family: {font_list} !important;
            }}
            .ant-table-tbody tr td div {{
                font-family: {font_list} !important;
            }}
            [style*="font-family"] {{
                font-family: {font_list} !important;
            }}
        `;
        document.head.appendChild(style);

        // 移除字体文件加载
        var fontLinks = document.querySelectorAll('link[href*=".woff"], link[href*=".ttf"], link[href*=".otf"]');
        fontLinks.forEach(function(link) {{
            link.remove();
        }});

        // 移除@font-face规则
        var styleSheets = document.styleSheets;
        for (var i = 0; i < styleSheets.length; i++) {{
            try {{
                var rules = styleSheets[i].cssRules || styleSheets[i].rules;
                for (var j = rules.length - 1; j >= 0; j--) {{
                    if (rules[j].type === CSSRule.FONT_FACE_RULE) {{
                        styleSheets[i].deleteRule(j);
                    }}
                }}
            }} catch (e) {{
                // 跨域样式表无法访问
            }}
        }}
        """

        try:
            driver.execute_script(script)
            logger.info("字体替换脚本注入成功")
        except Exception as e:
            logger.error(f"字体替换脚本注入失败: {e}")


class AdvancedFontDecoder:
    """高级字体解码器 - 使用更准确的方法"""

    def __init__(self):
        self.common_chinese_chars = {
            'uni4E00': '一', 'uni4E01': '丁', 'uni4E03': '七', 'uni4E08': '丈', 'uni4E09': '三',
            'uni4E0A': '上', 'uni4E0B': '下', 'uni4E0C': '不', 'uni4E0D': '与', 'uni4E0E': '丐',
            'uni4E0F': '丑', 'uni4E10': '专', 'uni4E11': '且', 'uni4E13': '世', 'uni4E14': '丘',
            'uni4E15': '丙', 'uni4E16': '业', 'uni4E17': '丛', 'uni4E18': '东', 'uni4E19': '丝',
            'uni4E1A': '丢', 'uni4E1B': '两', 'uni4E1C': '严', 'uni4E1D': '丧', 'uni4E1E': '个',
            'uni4E1F': '丫', 'uni4E20': '中', 'uni4E21': '丰', 'uni4E22': '串', 'uni4E23': '临',
            'uni4E24': '丸', 'uni4E25': '丹', 'uni4E26': '为', 'uni4E27': '主', 'uni4E28': '举',
            'uni4E29': '么', 'uni4E2A': '久', 'uni4E2B': '之', 'uni4E2C': '乌', 'uni4E2D': '乍',
            'uni4E2E': '乎', 'uni4E2F': '乏', 'uni4E30': '乐', 'uni4E31': '乒', 'uni4E32': '乓',
            'uni4E33': '乔', 'uni4E34': '乖', 'uni4E35': '乘', 'uni4E36': '乙', 'uni4E37': '乜',
            'uni4E38': '九', 'uni4E39': '也', 'uni4E3A': '习', 'uni4E3B': '乡', 'uni4E3C': '书',
            'uni4E3D': '买', 'uni4E3E': '乱', 'uni4E3F': '乳', 'uni4E40': '乾', 'uni4E41': '了',
            'uni4E42': '予', 'uni4E43': '争', 'uni4E44': '事', 'uni4E45': '二', 'uni4E46': '于',
            'uni4E47': '亏', 'uni4E48': '云', 'uni4E49': '互', 'uni4E4A': '五', 'uni4E4B': '井',
            'uni4E4C': '亘', 'uni4E4D': '些', 'uni4E4E': '亚', 'uni4E4F': '亟', 'uni4E50': '亡',
            # 数字映射
            'uni0030': '0', 'uni0031': '1', 'uni0032': '2', 'uni0033': '3', 'uni0034': '4',
            'uni0035': '5', 'uni0036': '6', 'uni0037': '7', 'uni0038': '8', 'uni0039': '9',
            # 常用标点
            'uni002D': '-', 'uni003A': ':', 'uni0020': ' ', 'uni002E': '.',
            # 时间相关
            'uni5E74': '年', 'uni6708': '月', 'uni65E5': '日', 'uni65F6': '时', 'uni5206': '分', 'uni79D2': '秒',
            # 常用词汇
            'uni676D': '杭', 'uni5DDE': '州', 'uni5E02': '市', 'uni533A': '区', 'uni53BF': '县',
            'uni6CD5': '法', 'uni9662': '院', 'uni52B3': '劳', 'uni52A8': '动', 'uni4EBA': '人',
            'uni6302': '挂', 'uni4E0D': '不', 'uni5230': '到', 'uni53F7': '号', 'uni901A': '通',
            'uni666E': '普', 'uni6B21': '次', 'uni4E0A': '上', 'uni4E0B': '下', 'uni4E2D': '中',
            'uni95EE': '问', 'uni9898': '题', 'uni5DE5': '工', 'uni4F5C': '作', 'uni65F6': '时',
            'uni95F4': '间', 'uni5730': '地', 'uni70B9': '点', 'uni529E': '办', 'uni516C': '公',
            'uni5BA4': '室', 'uni7535': '电', 'uni8BDD': '话', 'uni4F20': '传', 'uni771F': '真',
            'uni90AE': '邮', 'uni7BB1': '箱', 'uni7F51': '网', 'uni5740': '址', 'uni4EFB': '任',
            'uni804C': '职', 'uni8005': '者', 'uni6027': '性', 'uni522B': '别', 'uni51FA': '出',
            'uni751F': '生', 'uni5E74': '年', 'uni9F84': '龄', 'uni6559': '教', 'uni80B2': '育',
            'uni80CC': '背', 'uni666F': '景', 'uni5DE5': '工', 'uni4F5C': '作', 'uni7ECF': '经',
            'uni9A8C': '验', 'uni6295': '投', 'uni7B80': '简', 'uni5386': '历', 'uni5173': '关',
            'uni4E8E': '于', 'uni6211': '我', 'uni4EEC': '们', 'uni516C': '公', 'uni53F8': '司',
            'uni4F01': '企', 'uni4E1A': '业', 'uni4EA7': '产', 'uni54C1': '品', 'uni670D': '服',
            'uni52A1': '务', 'uni884C': '行', 'uni4E1A': '业', 'uni65B0': '新', 'uni95FB': '闻',
            'uni6D3B': '活', 'uni52A8': '动', 'uni8054': '联', 'uni7CFB': '系', 'uni65B9': '方',
            'uni5F0F': '式', 'uni9644': '附', 'uni4EF6': '件', 'uni4E0B': '下', 'uni8F7D': '载',
            'uni66F4': '更', 'uni591A': '多', 'uni4FE1': '信', 'uni606F': '息', 'uni8BF7': '请',
            'uni67E5': '查', 'uni770B': '看', 'uni8BE6': '详', 'uni7EC6': '细', 'uni5185': '内',
            'uni5BB9': '容', 'uni53D1': '发', 'uni5E03': '布', 'uni65F6': '时', 'uni95F4': '间',
            'uni6709': '有', 'uni6548': '效', 'uni671F': '期', 'uni81F3': '至', 'uni9690': '隐',
            'uni79C1': '私', 'uni653F': '政', 'uni7B56': '策', 'uni6CD5': '法', 'uni5F8B': '律',
            'uni58F0': '声', 'uni660E': '明', 'uni7248': '版', 'uni6743': '权', 'uni6240': '所',
            'uni5C4A': '届', 'uni6BD5': '毕', 'uni4E1A': '业', 'uni751F': '生', 'uni5B66': '学',
            'uni5386': '历', 'uni8981': '要', 'uni6C42': '求', 'uni5E94': '应', 'uni8058': '聘',
            'uni6761': '条', 'uni4EF6': '件', 'uni5DE5': '工', 'uni8D44': '资', 'uni5F85': '待',
            'uni9047': '遇', 'uni798F': '福', 'uni5229': '利', 'uni4FDD': '保', 'uni969C': '障',
            'uni793E': '社', 'uni4FDD': '保', 'uni516C': '公', 'uni79EF': '积', 'uni91D1': '金'
        }

    def decode_advanced_font(self, font_path: Path) -> Dict[str, str]:
        """高级字体解码方法"""
        try:
            from fontTools.ttLib import TTFont

            font = TTFont(font_path)
            cmap = font.getBestCmap()
            char_mapping = {}

            for unicode_val, glyph_name in cmap.items():
                char = chr(unicode_val)

                # 首先尝试从预定义映射表中查找
                if glyph_name in self.common_chinese_chars:
                    char_mapping[char] = self.common_chinese_chars[glyph_name]
                    continue

                # 尝试从字形名称解析数字
                digit_names = {
                    'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4',
                    'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9'
                }

                if glyph_name.lower() in digit_names:
                    char_mapping[char] = digit_names[glyph_name.lower()]
                    continue

                # 尝试从glyph名称中提取Unicode
                if 'uni' in glyph_name.lower():
                    uni_match = re.search(RegexPatterns.UNI_PATTERN, glyph_name, re.IGNORECASE)
                    if uni_match:
                        try:
                            real_unicode = int(uni_match.group(1), 16)
                            if (UnicodeRanges.CJK_UNIFIED_IDEOGRAPHS_START <= real_unicode <=
                                UnicodeRanges.CJK_UNIFIED_IDEOGRAPHS_END):  # 中文字符范围
                                real_char = chr(real_unicode)
                                char_mapping[char] = real_char
                                continue
                        except (ValueError, OverflowError):
                            pass

                # 如果是数字字符
                if glyph_name.startswith('u'):
                    try:
                        unicode_hex = glyph_name[1:]
                        real_unicode = int(unicode_hex, 16)
                        real_char = chr(real_unicode)
                        char_mapping[char] = real_char
                        continue
                    except (ValueError, OverflowError):
                        pass

                # 默认保持原字符
                char_mapping[char] = char

            logger.info(f"高级字体解码完成，映射了 {len(char_mapping)} 个字符")
            return char_mapping

        except ImportError:
            logger.error("需要安装 fontTools: pip install fonttools")
            return {}
        except Exception as e:
            logger.error(f"高级字体解码失败: {e}")
            return {}

    def analyze_text_patterns(self, text: str) -> Dict[str, int]:
        """分析文本模式，用于智能解码"""
        patterns = {
            'chinese_chars': 0,
            'numbers': 0,
            'punctuation': 0,
            'unknown_chars': 0
        }

        for char in text:
            unicode_val = ord(char)
            if (UnicodeRanges.CJK_UNIFIED_IDEOGRAPHS_START <= unicode_val <=
                UnicodeRanges.CJK_UNIFIED_IDEOGRAPHS_END):  # 中文字符
                patterns['chinese_chars'] += 1
            elif (UnicodeRanges.DIGITS_START <= unicode_val <=
                  UnicodeRanges.DIGITS_END):  # 数字字符
                patterns['numbers'] += 1
            elif char in '，。！？；：""''（）【】':
                patterns['punctuation'] += 1
            else:
                patterns['unknown_chars'] += 1

        return patterns

    def smart_decode(self, garbled_text: str, font_path: Path) -> str:
        """智能解码方法，结合多种策略"""
        if not garbled_text:
            return garbled_text

        # 分析文本模式
        patterns = self.analyze_text_patterns(garbled_text)

        # 如果乱码字符比例很高，尝试字体解码
        if patterns['unknown_chars'] > len(garbled_text) * TextAnalysisConstants.UNKNOWN_CHAR_THRESHOLD:
            mapping_table = self.decode_advanced_font(font_path)
            if mapping_table:
                decoded_text = ""
                for char in garbled_text:
                    decoded_text += mapping_table.get(char, char)
                return decoded_text

        return garbled_text


class FontAntiCrawlHandler:
    """字体反爬虫处理器 - 综合解决方案"""

    def __init__(self, cache_dir: str = FontConstants.DEFAULT_CACHE_DIR):
        self.font_decoder = FontDecoder(cache_dir)
        self.ocr_decoder = OCRDecoder()
        self.font_replacer = FontReplacer()
        self.advanced_decoder = AdvancedFontDecoder()

    def handle_font_obfuscation(self, driver, html_content: str, base_url: str) -> Dict[str, str]:
        """处理字体混淆的综合方案"""
        logger.info("开始处理字体反爬虫机制")

        # 策略1: 注入字体替换脚本
        self.font_replacer.inject_font_replacement_script(driver)

        # 策略2: 提取并解析字体文件
        font_urls = self.font_decoder.extract_font_urls(html_content, base_url)
        mapping_table = {}

        if font_urls:
            for font_url in font_urls:
                font_path = self.font_decoder.download_font_file(font_url)
                if font_path:
                    # 尝试常规解码
                    char_mapping = self.font_decoder.parse_woff_font(font_path)
                    mapping_table.update(char_mapping)

                    # 尝试高级解码
                    advanced_mapping = self.advanced_decoder.decode_advanced_font(font_path)
                    mapping_table.update(advanced_mapping)

        logger.info(f"字体反爬虫处理完成，获得 {len(mapping_table)} 个字符映射")
        return mapping_table

    def decode_page_text(self, driver, mapping_table: Dict[str, str],
                        element_selector: str = None) -> str:
        """解码页面文本"""
        try:
            if element_selector:
                # 解码特定元素
                from selenium.webdriver.common.by import By
                element = driver.find_element(By.CSS_SELECTOR, element_selector)
                text = element.text
            else:
                # 解码整个页面
                text = driver.find_element(By.TAG_NAME, "body").text

            # 应用字体映射解码
            if mapping_table:
                decoded_text = self.font_decoder.decode_text(text, mapping_table)
            else:
                decoded_text = text

            # 如果仍有乱码，尝试OCR
            if element_selector and self.ocr_decoder.ocr_available:
                ocr_text = self.ocr_decoder.decode_text_with_ocr(driver, element_selector)
                if ocr_text and len(ocr_text) > len(decoded_text) * OCRConstants.DEFAULT_CONFIDENCE:
                    decoded_text = ocr_text

            return decoded_text

        except Exception as e:
            logger.error(f"页面文本解码失败: {e}")
            return ""

    def get_debug_info(self) -> Dict:
        """获取调试信息"""
        return {
            'font_cache_dir': str(self.font_decoder.cache_dir),
            'cached_fonts': [f.name for f in self.font_decoder.cache_dir.glob('*')],
            'ocr_available': self.ocr_decoder.ocr_available,
            'font_mappings_count': len(self.font_decoder.font_mappings),
            'constants_loaded': True,
            'error_codes': {name: getattr(ErrorCodes, name) for name in dir(ErrorCodes) if not name.startswith('_')}
        }


# 使用示例
if __name__ == "__main__":
    # 初始化字体反爬虫处理器
    handler = FontAntiCrawlHandler()

    # 模拟使用场景
    sample_html = """
    <style>
    @font-face {
        font-family: 'CustomFont';
        src: url('/assets/font/custom.woff2') format('woff2');
    }
    </style>
    <div class="content" style="font-family: CustomFont;">这是被混淆的文本</div>
    """

    # 提取字体URL
    font_urls = handler.font_decoder.extract_font_urls(sample_html, "https://example.com")
    print(f"找到字体文件: {font_urls}")

    # 获取调试信息
    debug_info = handler.get_debug_info()
    print(f"调试信息: {debug_info}")
